#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif

#include <gtest4c.h>
#include <gconn.h>
#include <gigione.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <time.h>

#include <gigitest.h>


int g_closed_size = 0;
int g_idle_count  = 0;

void clean_timeout_socket_callback (void* pInfo, int nMsg, int lParam, int wParam)
{
  
  if (nMsg == GIGI_CB_CLEAN_TIMEOUT_CONN)
  {
    g_closed_size += lParam;
  }
  else if (nMsg == GIGI_CB_IN_IDLE)
  {
    g_idle_count++;
  }
}


COMM_TEST_WITH_CONN_STR (connPool, CleanTimeoutSocket, 16, "conn-expried=3")
{
  GIGI_SOCKET fd;
  int i;
  void* cb = NULL;
  
  if ( ctx->id == 0)
  {
    cb = gigi_set_callback (ctx->context, clean_timeout_socket_callback,0);
  }

  if (ctx->id != 0)
  {
    fd = create_channel (ctx, ctx->id, 0);
    C_ASSERT_TRUE ( fd );
    gigi_closesocket (fd, 0);
  }
  else
  {
    for (i = 1; i < ctx->count; i++)
    {
      fd = create_channel (ctx, i, 0);
      C_ASSERT_TRUE (fd );
      gigi_closesocket (fd, 0);
    }
  }

  if (ctx->id != 0)
  {
    GIGI_SLEEP (3000);
  }
  else
  {
    for (i =0; i < 30000 / 1000; i++)
    {
      if (g_closed_size +1 == ctx->count)
      {
        break;
      }

      GIGI_SLEEP (1000);
      printf ("closed (%d) : %d  %d\n", 
        (i+1)*1000, g_closed_size, g_idle_count);
    }

    printf ("CLOSED: %d\n", g_closed_size);
    C_ASSERT_TRUE ( ctx->count == (g_closed_size + 1) ) ;
  }

  if (cb)
  {
    gigi_kill_callback (cb);
  }
};


COMM_TEST_WITH_CONN_STR (connPool, MaxConnCount, 20, 
                         "max-count-of-pool=15;conn-expried=999999")
{
  GIGI_SOCKET fd;
  int i;

  if (ctx->id == 0)
  {
    for (i = 1; i < ctx->count; i++)
    {
      fd = create_channel (ctx, i, 0);
      C_ASSERT_TRUE (fd);
      gigi_closesocket (fd, 0);
    }
  }
  else
  {
    fd = create_channel (ctx, ctx->id, 0);
    C_ASSERT_TRUE (fd);
    gigi_closesocket (fd, 0);
  }
  if (ctx->id == 0)
  {
    C_ASSERT_TRUE (ctx->context->_conn_count == 15);
  }
}

COMM_TEST (connPool, RandCreateChannel,  8 )
{
  GIGI_SOCKET fd = 0, list=0;
  int Param[1000];
  int i, nSize, k, qq, nErr;
  int listCount = 0;
  int CreatePrnCount = 0;
  int ClosePrnCount = 0;
  int fromNode, toNode;
  GIGI_TIME tmStart = gigi_now ();

  C_ASSERT_TRUE ( (ctx->count & 1) == 0 );

  if (ctx->id & 1)
  {
    fromNode = ctx->id -1;
    toNode   = ctx->id;
  }
  else
  {
    fromNode = ctx->id;
    toNode   = ctx->id + 1;
  }

  for ( qq = 0; qq < 20; qq++)
  {
    C_ASSERT_TRUE (list == 0 && listCount == 0 );

    fd = create_channel (ctx,  fromNode,  toNode);

    C_ASSERT_TRUE ( fd );

    if (ctx->id == fromNode)
    {
      for (i =0; i<GIGI_COUNT_OF (Param); i++)
      {
        Param[i] = rand ();
      };
    }

    nSize = sizeof (Param);
    C_ASSERT_TRUE ( sendrecv (fd,  GIGI_TYPE_BUFFER, Param,  
                              &nSize, ctx->id,  fromNode, toNode) );

    nSize = sizeof (nSize);
    C_ASSERT_TRUE ( sendrecv (fd, GIGI_TYPE_INT32  , &nSize, NULL,
                              ctx->id, toNode, fromNode ) );

    nErr = gigi_closesocket (fd, 0);

    if (nErr)
    {
      printf ("ErrorCode1 = %d\n", nErr);
    }

    C_ASSERT_TRUE ( nErr == GIGI_OK);

    for (k = 0; k < GIGI_COUNT_OF (Param); )
    {
      unsigned a = Param[k++];
      unsigned b = Param[k++];

      if (a < (RAND_MAX/2))
      {
        b = (b < (RAND_MAX/2)) ? 1 : 0;
        fd = create_channel (ctx, b ? toNode : fromNode, b ? fromNode : toNode);
        C_ASSERT_TRUE ( fd );

        fd->_next = list;
        list = fd;

        listCount++;
        
        CreatePrnCount++;
        if (ctx->id == 0 && (CreatePrnCount%200) ==0 )
        {
          print_socket ("Create ",  ctx->id, fd);
        }
      }
      else if (list)
      {
        b /= (RAND_MAX / listCount);
        for (fd = list, i=0; (unsigned)i<b;i++)
        {
          fd = fd->_next ? fd->_next : list;
        };

        ClosePrnCount++;
        if (ctx->id == 0 && (ClosePrnCount % 200) == 0)
        {
          print_socket ("Close  ", ctx->id, fd);
        }

        C_ASSERT_TRUE ( _socket_list_del (&list, fd) );

        //print_socket ("Begin CloseSocket", ctx->id, fd);
        nErr = gigi_closesocket (fd, 0);

        if (nErr) printf ("ErrorCode2 = %d\n", nErr);

        C_ASSERT_TRUE ( nErr == GIGI_OK );
        
        listCount--;
      };
    };

    while (list)
    {
      fd = list;
      list = list->_next;
      listCount--;

      nErr = gigi_closesocket (fd, 0);

      if (nErr) printf ("ErrorCode3 = %d\n", nErr);

      C_ASSERT_TRUE ( nErr == GIGI_OK );
    };

    C_ASSERT_TRUE ( list ==0 && listCount == 0);

    printf ("Node:%d  %d finished\n", ctx->id,  GIGI_COUNT_OF (Param) * (qq+1) ); 
  }

  print_ctx_info (ctx);

  printf ("node:%d  tps = %d\n", ctx->id,
         gigi_calc_tps (tmStart, qq * ctx->count / 4 * GIGI_COUNT_OF (Param)));
}

COMM_TEST (APIs, OneNodeErrorCheck, 1)
{
  GIGI_SOCKET fd,fd2;
  GIGI_ID from;
  int nDiff;
  int nErrCode;
  GIGI_TIME now;
  GIGI_ID sID = GIGI_ID_MAKE1 (1000);

  printf ("connect to a invalid node\n");
  fd = gigi_socket (ctx->context, 100, ctx->service_id, 0, &nErrCode);
  C_ASSERT_TRUE (nErrCode == GIGI_E_CONNECT_ERROR);
  C_ASSERT_TRUE ( fd == NULL );

  printf ("connect to a invalid service id\n");
  fd = gigi_socket (ctx->context, ctx->node_id[0], sID, 0, &nErrCode);
  C_ASSERT_TRUE (nErrCode == GIGI_E_NO_SERVICE );
  C_ASSERT_TRUE (fd == NULL );

  printf ("accept a invalid service id\n");
  fd = gigi_accept (ctx->context, &from, 1000 ,-1);
  C_ASSERT_TRUE ( fd == NULL );
  
  printf ("accept timeout check 3000ms\n");
  now = gigi_now ();
  fd = gigi_accept (ctx->context, &from, ctx->service_id, 3000 );
  nDiff = gigi_time_diff (now);
  C_ASSERT_TRUE ( fd == NULL && nDiff < 3000 + 1000 && nDiff > 3000 - 1000 );

  printf ("connect to self test\n");
  fd = gigi_socket (ctx->context, ctx->node_id[0], ctx->service_id, 0, NULL);
  C_ASSERT_TRUE ( fd );
  gigi_closesocket (fd, GIGI_TRUE );

  fd2 = gigi_accept (ctx->context, &from, ctx->service_id, 3000 );
  C_ASSERT_TRUE (fd2 );
  gigi_closesocket (fd2, GIGI_TRUE);

  fd = gigi_socket (NULL, 0, 0, GIGI_TRUE, &nErrCode);
  C_ASSERT_TRUE ( nErrCode == GIGI_E_CONNECT_ERROR);
  C_ASSERT_TRUE ( fd == NULL);
};

COMM_TEST (APIs, TwoNodeErrorCheck, 2)
{
  GIGI_SOCKET fd;
    
  printf ("gigi_closesocket ( 0 ) <---> gigi_closesocket ( 1 ) \n");
  fd = create_channel (ctx, 0, 1);
  C_ASSERT_TRUE ( fd );
  gigi_closesocket (fd, ctx->id == 0 ? 0 : 1);

  printf ("gigi_closesocket ( 1 ) <---> gigi_closesocket ( 0 ) \n");
  fd = create_channel (ctx, 0, 1);
  C_ASSERT_TRUE ( fd );
  gigi_closesocket (fd, ctx->id == 0 ? 1 : 0);
};

COMM_TEST (APIs, rawMode, 2)
{
  GIGI_SOCKET fd;
  GIGI_ID addr;
  int i;
  int nErr;

  fd = create_channel (ctx, 0, 1);
  C_ASSERT_TRUE ( fd );

  addr = gigi_my_id (ctx->context);
  sendrecv (fd,GIGI_TYPE_ID, &addr, NULL,ctx->id, 1,0);

  gigi_closesocket (fd, 0);

  for (i = 0; i<1000; i++)
  {

    if (ctx->id == 0)
    {
      fd = gigi_socket (0,addr,ctx->service_id, GIGI_TRUE, &nErr);
      C_ASSERT_TRUE ( fd->_raw_mode );
    }
    else
    {      
      GIGI_ID from;
      fd = gigi_accept ( ctx->context, &from, ctx->service_id, 5000); 
      C_ASSERT_TRUE ( from == 0 );
      C_ASSERT_TRUE ( fd->_raw_mode );
    }

    C_ASSERT_TRUE ( fd );

    gigi_closesocket (fd, 0);
  }
};

COMM_TEST (APIs, MaxConnectForAccept, 2)
{
  GIGI_SOCKET fd;
  GIGI_SOCKET socks[256];
  int          socksLen;
  GIGI_ID addr;
  int i;
  int nErrCode = 0;

  fd = create_channel (ctx, 0, 1);
  C_ASSERT_TRUE ( fd );

  addr = gigi_my_id (ctx->context);
  sendrecv (fd,GIGI_TYPE_ID, &addr, NULL,ctx->id, 1,0);

  socksLen = 0;

  if ( ctx->id == 0 )
  {
    for (; socksLen < GIGI_COUNT_OF (socks) ; socksLen ++)
    {
      socks[ socksLen ] = gigi_socket (ctx->context, addr, ctx->service_id,
                                       GIGI_FALSE, &nErrCode);
      
      if (socks[socksLen] == NULL)
      {
        break;
      }
    }

    C_ASSERT_TRUE ( nErrCode == GIGI_E_SERVER_BUSY );
  }

  sendrecv (fd, GIGI_TYPE_INT32, &socksLen, NULL, ctx->id, 0, 1);

  if (ctx->id == 1)
  {
    for (i = 0; i < socksLen ; i ++)
    {
      GIGI_ID from;
      socks[i] = gigi_accept (ctx->context, &from, ctx->service_id, 5000);
    }
  }

  for (i = 0; i< socksLen; i++)
  {
    gigi_closesocket (socks[i], 0);
  };

  printf ("socket size =%d\n", socksLen );

  gigi_closesocket (fd, 0);
};

COMM_TEST (Stress, TwoNodeConnection, 50)
{
  GIGI_SOCKET fd;
  char buffer[256];
  int  bufferSize;
  int i, n;
  int fromNode, toNode;
  int vInt32;

  C_ASSERT_TRUE ( (ctx->count & 1) == 0 );

  if (ctx->id & 1)
  {
    fromNode = ctx->id - 1;
    toNode   = ctx->id;
  }
  else
  {
    fromNode = ctx->id;
    toNode   = ctx->id + 1;
  }

  for ( i = 0; i < 10; i++)
  {

    if (ctx->id == fromNode )
    {
      strcpy (buffer, "Message send to other node");
      bufferSize = (int)strlen (buffer) + 1;
    }
    else
    {
      bufferSize = sizeof (buffer);
    }

    //printf ("%2d begin create channel\n", ctx->id);
    fd = create_channel (ctx, fromNode, toNode);
    //print_socket ("create socket ", ctx->id, fd);

    n = sendrecv (fd, GIGI_TYPE_BUFFER, buffer, &bufferSize, 
                  ctx->id, fromNode, toNode);

    printf ("%d %s message (%d) \n",  ctx->id, 
            ctx->id ? "recv" : "send" ,  n );

    bufferSize = sizeof (vInt32);
    
    if (ctx->id == fromNode)
    {
      vInt32 = fromNode;
    }

    n = sendrecv (fd, GIGI_TYPE_INT32, &vInt32, NULL, 
                  ctx->id, fromNode, toNode);
    
    printf (" %d int=%d\n", ctx->id,  vInt32);

    //printf ("begin shutdown close at %d\n", ctx->id);

    n = gigi_closesocket (fd, 0);

    //printf ("gigi_closesocket %s at %d\n", n ? "Error" : "OK" ,  ctx->id);
  }

  for (i = 0; i<100;i++)
  {
    fd = create_channel (ctx,  fromNode,  toNode);
    //print_socket ("channel ",  ctx->id,  fd);
    gigi_closesocket (fd,  0);
  };

  print_ctx_info (ctx);
};

//     1, 2    1, 2     1, 3    1, 4    1, 5
//     1, 3    2, 3     2, 3    2, 4    2, 5
//     1, 4    2, 4     3, 4    3, 4    3, 5     
//     1, 5    2, 5     3, 5    4, 5    4, 5 
//
//     1.2    1.2     2.3    3.4    4.5
//     1.5    2.3     3.4    4.5    1.5
//
//     1.3    2.4     1.3    1.4    2.5 
//     1.4    2.5     3.5    2.4    3.5
//
//
COMM_TEST (Stress,  RingConnection,  67)
{
  GIGI_SOCKET fdIN, fdOUT;
  int v = ctx->id;
  int n;

  n =create_ring (ctx, &fdIN, &fdOUT);

  print_socket ("in  socket", ctx->id, fdIN);
  print_socket ("out socket", ctx->id, fdOUT);

  if (fdIN && fdOUT)
  {
    int vOut;
    sendrecv_ring (fdIN, fdOUT, ctx->id,  GIGI_TYPE_INT32,  &v, &vOut, 0);

    printf ("%d ring value=%d\n", ctx->id, vOut);
  }
  else
  {
    assert (0);
  }

  close_ring (ctx, fdIN, fdOUT);

  print_ctx_info (ctx);
}

C_TEST (APIs, basicTest)
{
  GIGIONE hComm = gigi_create ("port=12345","gigitest", 0, NULL);
  GIGI_SERVICE hServ;

  C_ASSERT_TRUE (hComm);

  C_ASSERT_TRUE ( gigi_create ("port=12345","gigitest", 0, NULL) == NULL);

  hServ = gigi_open_service (hComm, 1234, 34);
  C_ASSERT_TRUE ( hServ );

  C_ASSERT_TRUE ( NULL == gigi_open_service (hComm, 1234, 34) );
  
  gigi_close_service (hServ);

  gigi_free (hComm);
};

C_TEST_CASE_BEGIN (connPool_testCases)
  C_TEST_CASE (APIs,     OneNodeErrorCheck )
  C_TEST_CASE (APIs,     TwoNodeErrorCheck )
  C_TEST_CASE (APIs,     rawMode)
  C_TEST_CASE (APIs,     basicTest)
  C_TEST_CASE (APIs,     MaxConnectForAccept)
  C_TEST_CASE (Stress,   TwoNodeConnection)
  C_TEST_CASE (Stress,   RingConnection)
  C_TEST_CASE (connPool, RandCreateChannel)
  C_TEST_CASE (connPool, CleanTimeoutSocket)
  C_TEST_CASE (connPool, MaxConnCount)
C_TEST_CASE_END ()


